import hashlib
from typing import Optional, Union, List, Dict, Tuple

from beanie import PydanticObjectId
from fastapi import APIRouter, status, Response, Request
from fastapi.responses import PlainTextResponse

from auth.jwt.service import create_access_token
from auth.role.service import enforcer
from auth.user.model import User, UserIn, UserPermission, UserRole
from auth.user.service import current_user

router = APIRouter()


@router.post("/add", response_model=User)
async def add(obj: UserIn):
    obj.password = hashlib.sha256(obj.password.encode()).hexdigest()
    doc = await obj.insert()
    return doc


@router.delete("/permission")
async def delete_permission(obj: UserPermission):
    enforcer.delete_permission_for_user(obj.username, obj.path, obj.method)
    return {'detail': 'OK'}


@router.delete("/role")
async def delete_role(obj: UserRole):
    enforcer.delete_role_for_user(obj.username, obj.role_id)
    return {'detail': 'OK'}


@router.delete("/{_id}", response_model=User)
async def delete(_id: PydanticObjectId):
    doc = await User.get(_id)
    if doc:
        await doc.delete()
        return doc
    return PlainTextResponse(status_code=status.HTTP_404_NOT_FOUND, content='Not Found')


@router.put("/{_id}", response_model=User)
async def update(_id: PydanticObjectId, obj: UserIn):
    obj.password = hashlib.sha256(obj.password.encode()).hexdigest()
    update_query = {"$set": {k: v for k, v in obj.dict().items() if v is not None}}
    doc = await User.get(_id)
    if doc:
        await doc.update(update_query)
        return doc
    return PlainTextResponse(status_code=status.HTTP_404_NOT_FOUND, content='Not Found')


@router.get("/logout")
async def logout(response: Response):
    response.delete_cookie('X-Access-Token')
    return {'detail': 'OK'}


@router.get("/me", response_model=User)
async def get_me(request: Request):
    doc = await current_user(request)
    if doc:
        return doc
    return PlainTextResponse(status_code=status.HTTP_404_NOT_FOUND, content='Not Found')


@router.get("/{_id}", response_model=User)
async def get_one(_id: PydanticObjectId):
    doc = await User.get(_id)
    if doc:
        return doc
    return PlainTextResponse(status_code=status.HTTP_404_NOT_FOUND, content='Not Found')


@router.get("/", response_model=List[User])
async def get_all():
    data = await User.all().to_list()
    return data


@router.post("/list", response_model=Dict[str, Union[int, List[User]]])
async def get_list(
        args: Optional[dict] = {},
        page: int = 1, limit: int = 1000,
        sort: Optional[List[Tuple[str, int]]] = [('_id', -1)]):
    count = await User.find(args).count()
    data = await User.find(args, skip=(page-1)*limit, limit=limit, sort=sort).to_list()
    return {
        "count": count,
        "data": data
    }


@router.post("/login", response_model=User)
async def login(obj: UserIn, response: Response):
    doc = await UserIn.find_one(UserIn.username == obj.username)
    if doc and doc.password == hashlib.sha256(obj.password.encode()).hexdigest():
        token = create_access_token(obj.username)
        response.set_cookie(key="X-Access-Token", value=token)
        return doc
    return PlainTextResponse(status_code=status.HTTP_401_UNAUTHORIZED, content='Unauthorized')


@router.post("/permission")
async def add_permission(obj: UserPermission):
    enforcer.add_permission_for_user(obj.username, obj.path, obj.method)
    return {'detail': 'OK'}


@router.get("/{username}/permissions")
async def get_permissions(username: str):
    return enforcer.get_permissions_for_user(username)


@router.post("/role")
async def add_role(obj: UserRole):
    enforcer.add_role_for_user(obj.username, obj.role_id)
    return {'detail': 'OK'}


@router.get("/{username}/roles")
async def get_roles(username: str):
    return enforcer.get_roles_for_user(username)
